package models;

import java.util.*;

import javax.persistence.*;

import play.data.validation.Required;
import play.db.jpa.*;

/**
 * Relation Entity, Used to create relation between entities or topics
 * 
 * @author Menna Magdy
 */
@Entity
public class Relation extends Model {
	/**
	 * name of the relation
	 */
	@Required
	public String relationName;
	/**
	 * name of the source entity/topic
	 */
	@Required
	public String source;
	/**
	 * name of the destination entity/topic
	 */
	@Required
	public String destination;
	/**
	 * type of the relation whether between(entities/topics/tags)
	 */
	@Required
	public String type;
	/**
	 * date of the relation creation
	 */
	public Date postedAt;
	/**
	 * the relation can be created one user and the user created many
	 * organization
	 */
	@ManyToOne
	public User user;
	/**
	 * the organization can have many relations
	 * 
	 * @author Menna Magdy
	 */
	@ManyToOne
	public Organization organization;
	/**
	 * the entity can have many relations and every relation belongs to more
	 * than one entity
	 * 
	 * @author Menna Magdy
	 */
	@ManyToMany
	public List<Entities> entityList;
	/**
	 * the topic can have many relations & the every relation includes more than
	 * one topic
	 * 
	 * @author Menna Magdy
	 */
	@ManyToMany
	public List<Topic> topicList;
	/**
	 * the tag can have many relations & the every relation includes more than
	 * one tag
	 * 
	 * @author Menna Magdy
	 */
	@ManyToMany(mappedBy = "relationList")
	public List<Tag> tagList;

	/**
	 * Default constructor
	 * 
	 * 
	 * @param organization
	 *            organization of the relation
	 * @param user
	 *            Author of the relation
	 * @param relationName
	 *            name of the relation
	 * @param source
	 *            source of the relation
	 * @param destination
	 *            destination of the relation
	 * @author Menna Magdy
	 * @author Mai Selwanes only for the logging
	 */
	public Relation(Organization organization, User user, String relationName,
			String source, String destination, String type) {
		this.organization = organization;
		this.user = user;
		this.relationName = relationName;
		this.source = source;
		this.destination = destination;
		this.type = type;
		this.postedAt = new Date();
		this.entityList = new ArrayList<Entities>();
		this.topicList = new ArrayList<Topic>();
		this.tagList = new ArrayList<Tag>();
		Log log = new Log(organization, user, " has created a relation named "
				+ relationName + " between " + source + " and " + destination
				+ ".");
		log.save();
	}

	/**
	 * adds the source entity & the destination
	 * entity in the EntitiesList of the Relation and adds the Relation in the
	 * RelationList of each entity included
	 * 
	 * @author Menna Magdy
	 * @param List
	 *            <Entities>entity list of entities inside the organization
	 */
	public void addEntitiesRelation(List<Entities> entity) {
		Entities sourceEntity = null;
		Entities destinationEntity = null;
		for (int i = 0; i < entity.size(); i++) {
			if (entity.get(i).entityName.equalsIgnoreCase(source)) {
				sourceEntity = entity.get(i);
			}
			if (entity.get(i).entityName.equalsIgnoreCase(destination))
				destinationEntity = entity.get(i);
		}
		this.entityList.add(sourceEntity);
		this.entityList.add(destinationEntity);
		this.save();
		sourceEntity.relationList.add(this);
		destinationEntity.relationList.add(this);
		sourceEntity.save();
		destinationEntity.save();

	}

	/**
	 * adds the source Topic & the destination Topic in
	 * the topicList of the Relation and adds the Relation in the RelationList
	 * of each Topic included
	 * 
	 * @author Menna Magdy
	 */
	public void addTopicRelation(List<Topic> topic) {
		Topic sourceTopic = null;
		Topic destinationTopic = null;
		for (int i = 0; i < topic.size(); i++) {
			if (topic.get(i).title.equalsIgnoreCase(source))
				sourceTopic = topic.get(i);
			if (topic.get(i).title.equalsIgnoreCase(destination))
				destinationTopic = topic.get(i);
		}
		this.topicList.add(sourceTopic);
		this.topicList.add(destinationTopic);
		this.save();
		sourceTopic.relationList.add(this);
		destinationTopic.relationList.add(this);
		sourceTopic.save();
		destinationTopic.save();
	}

	/**
	 * adds the source entity & the destination entity in
	 * the tagList of the Relation and adds the Relation in the RelationList of
	 * each tag included
	 * 
	 * @author Menna Magdy
	 */
	public void addTagRelation(List<Tag> tag) {
		Tag sourceTag = null;
		Tag destinationTag = null;
		for (int i = 0; i < tag.size(); i++) {
			if (tag.get(i).name.equalsIgnoreCase(source))
				sourceTag = tag.get(i);
			if (tag.get(i).name.equalsIgnoreCase(destination))
				destinationTag = tag.get(i);
		}
		this.tagList.add(sourceTag);
		this.tagList.add(destinationTag);
		this.save();
		sourceTag.relationList.add(this);
		destinationTag.relationList.add(this);
		sourceTag.save();
		destinationTag.save();
	}

  
	/**
	 * renameRelation takes name of the relation the user want to change the
	 * existing relation to and check whether there is an already pre-defined
	 * relation or not , if yes the name of the relation will be renamed but if
	 * no, so there is no relation with this name
	 * 
	 * @param name
	 *            The new name that the user want to switch to
	 * @author Mai Abu-El Seoud
	 * @author Mai Selwanes only for the logging
	 */
/*
	public void renameRelation(String name) {
		String oldName = this.relationName;
		Log log = new Log(organization, user, " has edited the relation named "
				+ oldName + " to be " + name + ".");
		log.save();
		Boolean found = false;
		for (int i = 0; i <= RelationNames.getAllRelationNames().size(); i++) {
			if (RelationNames.getAllRelationNames().get(i).relationName
					.equals(name)) {
				this.relationName = name;
				found = true;
			}
		}
		if (found == false) {
			System.out.println("Sorry this relation name does not exist!!");
		}
	}
	*/

	/**
	 * This method search for relations by name, the user enters the relaionName
	 * 
	 * @param search
	 *            the relationName that the user wants to search for
	 * @param page
	 *            the number of page in the results list that the user wants
	 * @return returning the searchResults which contains the results' list and
	 *         their number
	 * @author Nourhan ElNehlawy
	 */

	public static SearchResults searchForRelationsName(String search,
			Integer page) {
		String searchOne = search.toLowerCase();
		long count = count("LOWER (relationName) LIKE ?", searchOne);
		List<Relation> items = find("LOWER (relationName) LIKE ?", searchOne).fetch(page,
				20);
		return new SearchResults(items, count);
	}

	/**
	 * This method searches for entities through relations by specifying source,relationName and destination
	 * 
	 * @param src
	 *            the relation's source
	 * @param relName
	 *            the relation's name
	 * @param dest
	 *            the relation's destination
	 * @param page
	 *            the results' page
	 * @param entType
	 *              the type of the source and destination, which is here entity
	 * @return
	 *         returning the searchResults which contains the results' list and
	 *         their number
	 * @author Nourhan ElNehlawy
	 */
	public static SearchResults searchForRelationsSourceDestinationEntity(String source1,
			String relationName1, String destination1, Integer page, String entityType) {
		String source = source1.toLowerCase();
		String relationName = relationName1.toLowerCase();
		String destination = destination1.toLowerCase();
		long count;
		List<Relation> items;
		
		if(destination.equals("")){
		 count = count(
				"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? OR LOWER (destination) LIKE ? AND type LIKE ? ",
				relationName, source,destination, entityType);
		 items = find(
				"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? OR LOWER (destination) LIKE ? AND type LIKE ? ",
				relationName, source,destination, entityType).fetch(page, 20);
		}
		else if(source.equals("")){
			count = count(
					"LOWER (relationName) LIKE ? AND LOWER (destination) LIKE ? OR LOWER (source) LIKE ? AND type LIKE ? ",
					relationName, destination, source, entityType);
			 items = find(
					"LOWER (relationName) LIKE ? AND LOWER (destination) LIKE ? OR LOWER (source) LIKE ? AND type LIKE ? ",
					relationName,destination, source, entityType).fetch(page, 20);
		}
		else{
			count = count(
					"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? AND LOWER (destination) LIKE ? AND type LIKE ? ",
					relationName, source,destination, entityType);
			 items = find(
					"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? AND LOWER (destination) LIKE ? AND type LIKE ? ",
					relationName, source, destination, entityType).fetch(page, 20);
		}
		
		return new SearchResults(items, count);
	}
	/**
	 * This method searches for topics through relations by specifying source,relationName and destination
	 * 
	 * @param src
	 *            the relation's source
	 * @param relName
	 *            the relation's name
	 * @param dest
	 *            the relation's destination
	 * @param page
	 *            the results' page
	 * @param entType
	 *              the type of the source and destination, which is here topic
	 * @return
	 *        returning the searchResults which contains the results' list and
	 *         their number
	 * @author Nourhan ElNehlawy
	 */
	public static SearchResults searchForRelationsSourceDestinationTopic(String source1,
			String relationName1, String destination1, Integer page, String topicType) {
		String source = source1.toLowerCase();
		String relationName = relationName1.toLowerCase();
		String destination = destination1.toLowerCase();
		long count;
		List<Relation> items;
		
		if(destination.equals("")){
		 count = count(
				"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? OR LOWER (destination) LIKE ? AND type LIKE ? ",
				relationName, source,destination, topicType);
		 items = find(
				"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? OR LOWER (destination) LIKE ? AND type LIKE ? ",
				relationName, source,destination, topicType).fetch(page, 20);
		}
		else if(source.equals("")){
			count = count(
					"LOWER (relationName) LIKE ? AND LOWER (destination) LIKE ? OR LOWER (source) LIKE ? AND type LIKE ? ",
					relationName, destination, source, topicType);
			 items = find(
					"LOWER (relationName) LIKE ? AND LOWER (destination) LIKE ? OR LOWER (source) LIKE ? AND type LIKE ? ",
					relationName,destination, source, topicType).fetch(page, 20);
		}
		else{
			count = count(
					"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? AND LOWER (destination) LIKE ? AND type LIKE ? ",
					relationName, source,destination, topicType);
			 items = find(
					"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? AND LOWER (destination) LIKE ? AND type LIKE ? ",
					relationName, source, destination, topicType).fetch(page, 20);
		}
		return new SearchResults(items, count);
	}
	/**
	 * This method searches for tags through relations by specifying source,relationName and destination
	 * 
	 * @param src
	 *            the relation's source
	 * @param relName
	 *            the relation's name
	 * @param dest
	 *            the relation's destination
	 * @param page
	 *            the results' page
	 * @param entType
	 *              the type of the source and destination, which is here tag
	 * @return
	 *        returning the searchResults which contains the results' list and
	 *         their number
	 * @author Nourhan ElNehlawy
	 */
	public static SearchResults searchForRelationsSourceDestinationTag(String source1,
			String relationName1, String destination1, Integer page, String tagType) {
		String source = source1.toLowerCase();
		String relationName = relationName1.toLowerCase();
		String destination = destination1.toLowerCase();
		long count;
		List<Relation> items;
		
		if(destination.equals("")){
		 count = count(
				"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? OR LOWER (destination) LIKE ? AND type LIKE ? ",
				relationName, source,destination, tagType);
		 items = find(
				"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? OR LOWER (destination) LIKE ? AND type LIKE ? ",
				relationName, source,destination, tagType).fetch(page, 20);
		}
		else if(source.equals("")){
			count = count(
					"LOWER (relationName) LIKE ? AND LOWER (destination) LIKE ? OR LOWER (source) LIKE ? AND type LIKE ? ",
					relationName, destination, source, tagType);
			 items = find(
					"LOWER (relationName) LIKE ? AND LOWER (destination) LIKE ? OR LOWER (source) LIKE ? AND type LIKE ? ",
					relationName,destination, source, tagType).fetch(page, 20);
		}
		else{
			count = count(
					"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? AND LOWER (destination) LIKE ? AND type LIKE ? ",
					relationName, source,destination, tagType);
			 items = find(
					"LOWER (relationName) LIKE ? AND LOWER (source) LIKE ? AND LOWER (destination) LIKE ? AND type LIKE ? ",
					relationName, source, destination, tagType).fetch(page, 20);
		}
		return new SearchResults(items, count);
	}
	/**
	 * This method returns a list of relations whose source and destination are of type Entity
	 * @return
	 *      returning the list of relations
	 * @author Nourhan El-Nehlawy
	 */
	public static List<Relation> chooseEntityRelation(){
		List<Relation> entityRelationList = find("type LIKE ?", "entity").fetch();
		return entityRelationList;
	}
	/**
	 * This method returns a list of relations whose source and destination are of type Topic
	 * @return
	 *      returning the list of relations
	 * @author Nourhan El-Nehlawy
	 */
	
	public static List<Relation> chooseTopicRelation(){
		List<Relation> topicRelationList = find("type LIKE ?", "topic").fetch();
		return topicRelationList;
	}
	/**
	 * This method returns a list of relations whose source and destination are of type Tag
	 * @return
	 *      returning the list of relations
	 * @author Nourhan El-Nehlawy
	 */
	
	public static List<Relation> chooseTagRelation(){
		List<Relation> tagRelationList = find("type LIKE ?", "tag").fetch();
		return tagRelationList;
	}

	/**
	 * This method takes the user id and returns true if the type of the organization
	 * that he's searching in is public ,
	 * OR False if the organization is private/Closed and he's Not a member in it and True otherwise
	 * @param userId
	 *            the user's id
	 * @return
	 *       returns the True or False
	 *       @author Nourhan El-Nehlawy
	 */
	public boolean checkType(Long userId){
		if(!this.organization.type.matches("Public")){
			User user = User.findById(userId);
				if(this.organization.checkUser(user)){
					return true;
				}
				else return false;
		}
		else return true;
	}
	

	
	/**
	 * checks the type of the relation that should be unassigned to
	 * call the appropriate method to execute it.
	 * 
	 * 
	 * @author Mai Abu-El Seoud
	 */

	public void checkRelationType() {
		if (this.type.equals("entity")) {
			unassignEntityRelation();
		} else {
			if (this.type.equals("topic")) {
				unassignTopicRelation();
			} else {
				unassignTagRelation();
			}
		}
	}

	/**
	 * creates a list of all topics and loops on it to find in
	 * which index the source and destination topics are and then erase them from
	 * the topicList and remove the relation from the topic.relationList
	 * 
	 * 
	 * @author Mai Abu-El Seoud
	 */
	public void unassignTopicRelation() {
		Organization organization = this.organization;
		LinkedList<Topic> allTopics = new LinkedList<Topic>();
		for (int i = 0; i < organization.allEntities.size(); i++) {
			for (int j = 0; j < organization.allEntities.get(i).topics.size(); j++) {
				allTopics.add(organization.allEntities.get(i).topics.get(j));
			}
		}
		Topic destinationTopic = null;
		Topic sourceTopic = null;
		for (int i = 0; i < allTopics.size(); i++) {
			if (allTopics.get(i).title.equalsIgnoreCase(this.source));
				sourceTopic = allTopics.get(i);
			
			if (allTopics.get(i).title.equalsIgnoreCase(this.destination));
				destinationTopic =allTopics.get(i); 
			}
		
		sourceTopic.relationList.remove(this);
		destinationTopic.relationList.remove(this);
		this.topicList.remove(sourceTopic);
		this.topicList.remove(destinationTopic);
		this.organization.relationList.remove(this);
		this.organization.save();
		this.organization= null;
		sourceTopic.save();
		destinationTopic.save();
		this.user=null;
		this.save();
		this.delete();
		
		
	}
	/**
	 * creates a list of all entites and loops on it to find in
	 * which index the source and destination entities are and then erase them
	 * from the entityList and remove the relation from the entity.relationList
	 * 
	 * @author Mai Abu-El Seoud
	 */
	public void unassignEntityRelation() {
		List<Entities> allEntities = this.organization.allEntities;
		Entities destinationEntity = null;
		Entities sourceEntity = null;
		for (int i = 0; i < allEntities.size(); i++) {
			if (allEntities.get(i).entityName.equalsIgnoreCase(this.source));
				sourceEntity = allEntities.get(i);
			
			if (allEntities.get(i).entityName.equalsIgnoreCase(this.destination));
				destinationEntity =allEntities.get(i); 
			}
		
		sourceEntity.relationList.remove(this);
		destinationEntity.relationList.remove(this);
		this.entityList.remove(sourceEntity);
		this.entityList.remove(destinationEntity);
		this.organization.relationList.remove(this);
		this.organization.save();
		this.organization= null;
		sourceEntity.save();
		destinationEntity.save();
		this.user=null;
		this.save();
		this.delete();
		
		
	}

	/**
	 * creates a list of all tags and loops on it to find in which
	 * index the souce and destination tags are and then erase them from the
	 * tagList and remove the relation from the tag.relationList
	 * 
	 * @author Mai Abu-El Seoud
	 * 
	 * 
	 */
		public void unassignTagRelation() {
			List<Tag> allTags = this.organization.tags;
			Tag destinationTag=null;
			Tag sourceTag=null;
			for (int i = 0; i < allTags.size(); i++) {
				if (allTags.get(i).name.equalsIgnoreCase(this.source)) 
						sourceTag=allTags.get(i);
				if (allTags.get(i).name.equalsIgnoreCase(this.destination)) 
						destinationTag=allTags.get(i);
			}
			sourceTag.relationList.remove(this);
			destinationTag.relationList.remove(this);
			this.tagList.remove(sourceTag);
			this.tagList.remove(destinationTag);
			this.organization.relationList.remove(this);
			this.organization.save();
			this.organization=null;
			sourceTag.save();
			destinationTag.save();
			this.save();
			this.delete();
		
		
			}

	/**
	 * Method search in Relations returns the list of Relations with names that
	 * match the given string parameter search to search the keyword that will
	 * be used to be fetched from the database
	 * 
	 * @author Ahmed Ammar
	 */
	public static List<Relation> search(String search) {
		String likeSearch = search.toLowerCase();
		return Relation.find("LOWER (relationName) LIKE ?", likeSearch).fetch();
	}
}
